package com.yimu.ymblog.common.service.impl;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.yimu.ymblog.common.service.RedisService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 *  redis 工具类
 *
 **/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Service
@RequiredArgsConstructor
public class RedisServiceImpl implements RedisService {

    public final RedisTemplate redisTemplate;

    // Jackson的ObjectMapper用于JSON序列化和反序列化
    public final ObjectMapper objectMapper;

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    @Override
    public  void setCacheObject(final String key, final Object value)
    {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    @Override
    public void setCacheObject(final String key, final Object value, final Integer timeout, final TimeUnit timeUnit)
    {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 缓存value值为json的数据
     *
     * @param key
     * @param value
     */
    @Override
    public void setJsonObject(String key, Object value) {
        try {
            // 将Java对象序列化为JSON字符串
            String jsonString = objectMapper.writeValueAsString(value);
            // 存储JSON字符串到Redis
            ValueOperations<String, String> ops = redisTemplate.opsForValue();
            ops.set(key, jsonString);
        } catch (Exception e) {
            // 处理异常
            e.printStackTrace();
        }
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功；false=设置失败
     */
    @Override
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return redisTemplate.expire(key, timeout, unit);
    }

    @Override
    public Set<Object> getCacheSet(final String key)
    {
        return redisTemplate.boundSetOps(key).members();
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    @Override
    public Object getCacheObject(final String key)
    {
        ValueOperations<String, Object> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 获得缓存的json数据的value转成Object对象
     *
     * @param key
     * @return
     */
    @Override
    public Object getCacheJsonObject(String key) {
        try {
            String jsonString = (String) redisTemplate.opsForValue().get(key);
            // 将JSON字符串反序列化为Java对象
            Object object = objectMapper.readValue(jsonString, Object.class);
            return object;
        } catch (Exception e) {
            // 处理异常
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将json字符串添加到列表中
     *
     * @param key
     * @param value
     */
    @Override
    public void setObjectToJsonList(String key, Object value) {
        try {
            // 将Java对象序列化为JSON字符串
            String jsonString = objectMapper.writeValueAsString(value);

            // 获取列表操作
            ListOperations<String, String> listOps = redisTemplate.opsForList();

            // 将JSON字符串添加到列表中（在列表的末尾）
            listOps.rightPush(key, jsonString);
        } catch (Exception e) {
            // 处理异常
            e.printStackTrace();
        }
    }



    /**
     * 获取列表中的所有JSON字符串
     * @param key
     * @return
     */
    public <T> List<T> getAllJsonToObjectList(String key, Class<T> clazz) {
        // 获取列表操作
        ListOperations<String, String> listOps = redisTemplate.opsForList();

        // 从列表中获取所有JSON字符串元素
        List<String> jsonStrings = listOps.range(key, 0, -1);

        // 使用Jackson的ObjectMapper反序列化每个JSON字符串为MyObject对象
        return jsonStrings.stream()
                .map(json -> {
                    try {
                        return objectMapper.readValue(json, clazz);
                    } catch (Exception e) {
                        // 处理解析异常，根据实际情况处理，这里简单地记录日志或抛出运行时异常
                        throw new RuntimeException("无法将Json转为Object对象", e);
                    }
                })
                .collect(Collectors.toList());
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    @Override
    public boolean deleteObject(final String key)
    {
        return redisTemplate.delete(key);
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public long deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection);
    }

    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    @Override
    public long setCacheList(final String key, final List<Object> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    @Override
    public List<Object> getCacheList(final String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    @Override
    public void setCacheMap(final String key, final Map<String, Object> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    @Override
    public Map<String, Object> getCacheMap(final String key)
    {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public void setCacheHashValue(String key, String hKey, Object value) {
        try {
            // 将Java对象序列化为JSON字符串
            String jsonString = objectMapper.writeValueAsString(value);
            redisTemplate.opsForHash().put(key, hKey, jsonString);
        } catch (Exception e) {
            // 处理异常
            e.printStackTrace();
        }
    }



    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> List<T> getCacheHashList(String key, String hKey, Class<T> clazz)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        List<T> jsonStrings = opsForHash.values(key);
        return jsonStrings.stream()
                .map(json -> {
                    try {
                        return objectMapper.readValue(json.toString(), clazz);
                    } catch (Exception e) {
                        // 处理解析异常，根据实际情况处理，这里简单地记录日志或抛出运行时异常
                        throw new RuntimeException("无法将Json转为Object对象", e);
                    }
                })
                .collect(Collectors.toList());
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheHashValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }



    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern)
    {
        return redisTemplate.keys(pattern);
    }

    /**
     * 返回集合key1中存在，但是key2中不存在的数据集合  sdiff
     *
     * @param key1
     * @param key2
     * @return
     */
    @Override
    public Set<String> diff(String key1, String key2) {
        return redisTemplate.opsForSet().difference(key1, key2);
    }

    @Override
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    @Override
    public Long hIncr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, delta);
    }

    @Override
    public Long hDecr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, -delta);
    }

    @Override
    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    @Override
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    @Override
    public Object hGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    @Override
    public Long incr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    @Override
    public Long sAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    /**
     * 返回一个set类型key下的数量
     * @param key
     * @return
     */
    @Override
    public Long getCacheSetKeyNumber(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    @Override
    public RedisTemplate getRedisTemplate() {
        return this.redisTemplate;
    }

    /**
     *  增加文字阅读量或标签点击量
     * @return
     */
    @Override
    @Async("threadPoolTaskExecutor")
    public void incrArticle(Long id,String key,String ip) {
        Map<String, Object> map = getCacheMap(key);
        List<String> ipList = (List<String> ) map.get(id.toString());
        if (ipList != null) {
            if (!ipList.contains(ip)) {
                ipList.add(ip);
            }
        }else {
            ipList = new ArrayList<>();
            ipList.add(ip);
        }
        map.put(id.toString(),ipList);
        setCacheMap(key,map);
    }

    @Override
    public void redisTimer() {
        redisTemplate.opsForValue().get("heartbeat");
    }

    @Override
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 判断是否存在某个hashKey
     *
     * @param key
     * @param hashKey
     * @return
     */
    @Override
    public boolean hasHashKey(String key, String hashKey) {
        Set keys = redisTemplate.opsForHash().keys(key);
        boolean b = keys.stream().anyMatch(k -> k.equals(hashKey));
        return b;
    }


}
